# _*_coding:utf-8_*_
import sys
import os
import traceback
from utils.log_util import loggings as logger
import copy
from ATS.globaldata import gdata
from ATS.errHandler import errHandler
from ATS.util import constant
from ATS.datamanager import DbImpl
from ATS.datamanager import DbServer
import configparser as ConfigParser

CP_OBJ = ConfigParser.ConfigParser()

workpath = os.path.dirname(os.path.realpath(sys.argv[0]))
sys.path.insert(0, os.path.dirname(workpath))

gui_setting_f = os.path.join(constant.GUI_PATH, 'GUI_Setting.ini')
configparser_obj = ConfigParser.ConfigParser()

Default_Alarming = 100
DM = None


def get_DM():
    """
    Only 1 EEManager instance is existed in the ATS system.
    Call get_EEM to get the single instance.
    """
    global DM
    if DM is None:
        DM = DataManager()
    return DM


def get_ALARM_CNT():
    """
    FUNCTION: get the continue fail count configured in the GUI
    If continue fail count reached to this number,send real time data immediately.
    Default ALARM_CNT=5
    """
    global gui_setting_f
    global configparser_obj
    configparser_obj.read(gui_setting_f)
    try:
        setting = configparser_obj.get('Recent_PPF', 'config_setting')
    except:
        # print(traceback.format_exc())
        return Default_Alarming
    if setting is None:
        return Default_Alarming

    s_list = setting.split("@")
    return int(s_list[4])


class DataManager:
    def __init__(self, ee_obj_name='ExecEngine1'):
        self.clear()
        self.ee_obj_name = ee_obj_name
        self.global_service = {}

    def clear(self):
        self.data = {}

    def set_test(self, task_obj):
        self.exec_task = task_obj

    def postSingleData(self, var, value):
        """
        Post single point data
        """
        if var not in self.data:
            self.data[var] = []
        self.data[var].append(value)
        self.log_to_persist_model()

    def postdata(self, var, valuelist):
        self.data[var].extend(valuelist)
        # print("111",self.data)
        gdd = gdata.getGDD()
        gdd['real_time_data'][var] = valuelist
        # print("22222",gdd)

    def postarraydata(self, var, valuelist):
        """
        Post multi point data
        """
        if var not in self.data:
            self.data[var] = []
        self.data[var].extend(valuelist)
        self.log_to_persist_model()
        # if constant.CUSTOMIZE_FOR_LOOP:
        gdd = gdata.getGDD()
        gdd['real_time_data'][var] = valuelist
        if var.endswith('_PorF'):
            key = "%s_failcount" % var[:-5]
            if key not in gdd['real_time_data']:
                gdd['real_time_data'][key] = 0


    def postAllDataForOneLoop(self, data):
        """
        data: {
        'CS':[1,2,3,4],'RS':[5,6,7,8],'CS_PorF':[1],'RS_PorF':[0]}
        """
        gdd = gdata.getGDD()
        for k in data.keys():
            if k not in self.data:
                self.data[k] = []
            v = data.get(k)
            if isinstance(v, list):
                self.data[k].extend(v)
            else:
                self.data[k].append(v)

            if constant.CUSTOMIZE_FOR_LOOP:
                # record the real time data Cs/Rs/Cs_PorF/Cs_failcount...
                gdd['real_time_data'][k] = data[k]
                if k.endswith('_PorF'):
                    key = "%s_failcount" % k[:-5]
                    if key not in gdd['real_time_data']:
                        gdd['real_time_data'][key] = 0
                    # calculate continue fail flag
                    key2 = "%s_continuefailcount" % k[:-5]
                    if key2 not in gdd['real_time_data']:
                        gdd['real_time_data'][key2] = 0
                    if not data[k]:
                        gdd['real_time_data'][key] += 1
                        gdd['real_time_data'][key2] += 1
                        if gdd['real_time_data'][key2] >= get_ALARM_CNT():
                            # now this flag is used to stop robert hand.
                            gdd['NOT_END'] = True
                    else:
                        gdd['real_time_data'][key2] = 0  # if unfailed, continue is reset
        self.log_to_persist_model()

    def log_to_persist_model(self):
        if constant.ENABLE_DB_SERVER:
            # normal_things = {'locid': 1, 'lopid': 1}
            normal_things = {'locid': self.exec_task.get_location_ind(), 'lopid': self.exec_task.loopId}
            va = {constant.DB_INSERT_TYPE: (normal_things, copy.deepcopy(self.data))}
            DbServer.add_task(va)
        else:
            self.log_to_persist_model_n()

    def log_to_persist_model_n(self):
        curr_db_file = self.global_service.get(constant.CURR_DB_FILE, None)
        data_model = gdata.get_service(constant.DATA_MODEL_INSTANCE)
        new_model = None
        try:
            new_model = DbImpl.DbImpl(curr_db_file)
            new_model.copyNew(data_model)
            if new_model is None:
                logger.error("Error in log_to_persist_model: new_model object is None")
                return
            location_pkid = None
            ind = self.exec_task.get_location_ind()
            location = gdata.getLMD().get(ind, None)
            #            if self.exec_task.loopId is None:
            print("ind", ind)
            print("location", location)
            index_loc_map = gdata.getGDD().get(constant.INDEX_LOC_MAP, {})
            location_pkid = index_loc_map.get(ind, None)
            print("location_pkid", location_pkid)
            if location_pkid:
                new_model.persist_data(location_pkid, location, self.data)
        except:
            logger.error("Error in log_to_persist_model: " + traceback.format_exc())
        finally:
            if new_model:
                new_model.release_con()

    def registerBinResult(self, dev, loop, bin_value):
        vvv = {'dev': dev, 'loop': loop, 'bin_value': bin_value}
        va = {constant.DB_UPDATE_TYPE: vvv}
        DbServer.add_task(va)
        if constant.CUSTOMIZE_FOR_LOOP:
            self.cal_realtime_bin(bin_value)

    def cal_realtime_bin(self, bin_value):
        """
        Customized methond for i-robert package level repeat test
        """
        gdd = gdata.getGDD()
        # add 1 for current bin_value
        if bin_value not in gdd['real_time_data']:
            gdd['real_time_data'][bin_value] = 0
        gdd['real_time_data'][bin_value] += 1

        all_bins = 0
        for i in range(1, 5, 1):
            if 'BIN%s' % i in gdd['real_time_data']:
                all_bins += gdd['real_time_data']['BIN%s' % i]
        if all_bins > 0:
            for i in range(1, 5, 1):
                if 'BIN%s' % i in gdd['real_time_data']:
                    gdd['real_time_data']['BIN%s_percent' % i] = gdd['real_time_data']['BIN%s' % i] / float(all_bins)

    def getCurIni(self):
        ini_dict = {}
        ppf = self.getgdata('PPF')
        if ppf is None:
            return ini_dict

        ini_f = os.path.join(constant.INI_PATH, ppf[:-4] + '.ini')
        if not os.path.exists(ini_f):
            return ini_dict
        else:
            # read ini file, and return configuration value
            global CP_OBJ
            CP_OBJ.read(ini_f)
            for sec in CP_OBJ.sections():
                if sec not in ini_dict:
                    ini_dict[sec] = {}
                ini_dict[sec].update(CP_OBJ.items(sec))
        return ini_dict

    def getgdata(self, var):
        """
        call this method to get global data by name: var
        """
        tmp_d = gdata.getGDD()
        if var in tmp_d:
            return tmp_d[var]
        else:
            return None

    def addgdata(self, var, value):
        """
        call this method to update global data pool
        """
        try:
            tmp_d = {var: value}
            gdata.updateGDD(tmp_d)
        except:
            logger.error(f'{self.ee_obj_name} addgdata')

    def posterror(self, errmsg):
        """
        test script can call this method to myLog error
        """
        # post error to the myLog file
        logger.error(f'{self.ee_obj_name} posterror')
